<?php

/***  DOCUMENTATION LAYER

CEO Element Parent Class

Name: CeoFormElement
Last Update: Jul 2007
Author: Tom at klenwell@gmail.com

DESCRIPTION
  An abstract for various form element classes

METHODS
  MAGIC
  CeoFormElement($name, $type, $label, $is_required=0, $arg5=null, $arg6=null, $arg7=null, $arg8=null)    *php 4 constructor*
  __construct($name, $type, $label, $is_required, $arg5, $arg6, $arg7, $arg8)    *php 5 constructor*
  __destruct()  
  
  PUBLIC
  filter_input($text, $WHITE_TAG=null, $WHITE_ATT=null, $debug=0)
  is_submitted()
  set_valid_value($value)
  unset_valid_value()
  get_valid_value()
  invalidate()
  reset()
  print_block()
  get_valid_types()
  static_build_hidden_input($name, $value)
  print_d($message, $color='#c33')
  print_r($Mixed)
  dump()
  
  PRIVATE
  _process_input()
  _validate_input()
  _get_display_value()
  _set_session_value()
  _clear_session_value()
  _get_session_value()
  _cx_is_valid()
  _wrap_print_block($core_html, $type, $name, $id=null, $class=null)
  _override_warning($fx='?')
  _set_session_data()
  _get_session_data()
  _has_session_data()
  _is_valid_email($email)
  _is_valid_url($url)
  _set_filename()
  _set_dirpath()
  
USAGE
  designed to be called through CeoForm method add_element()

NOTES
  Each element object is constructed by the CeoForm object.  After constructing it,
  CeoForm object passes the Element object a *reference* to its InputFilter
  object.
  
  Since the type is validated by both this class and the factory class, it would
  make sense for them to refer to the same validation array.  And that's just 
  what they do, thanks to the get_valid_types() method, which may be called
  statically: CeoFormElement::get_valid_types()

  The InputFilter class comes from http://cyberai.com/inputfilter/ (now defunct?)
  by Daniel Morris
    
    From the docs: 
      Instantiate the class with your settings.
      1st (tags array):    Optional (since 1.2.0)
      2nd (attr array):    Optional
      3rd (tags method):   0 = remove ALL BUT these tags (default)
                           1 = remove ONLY these tags
      4th (attr method):   0 = remove ALL BUT these attributes (default)
                           1 = remove ONLY these attributes
      5th (xss autostrip): 1 = remove all identified problem tags (default)
                           0 = turn this feature off
    
    $myFilter = new InputFilter($tags, $attributes, $is_tag_blacklist, $is_attribute_blacklist);

______________________________________________________________________________*/

// Load File of Base Class


// CeoFormElement
/*____________________________________________________________________________*/
class CeoFormElement
{
/* PUBLIC PROPERTIES */
var $debug = 0;
var $class_name = __CLASS__;
var $oid = '';
var $DS = DIRECTORY_SEPARATOR;

// arrays
var $VALID = array();

// basic arguments
var $type = '';
var $name = '';
var $label = '';
var $is_required = 0;

// public properties
var $is_valid = 0;
var $valid_input = null;
var $class = '';
var $id = '';
var $style = '';
var $css_class = '';

// fixed parameters
var $svc = 'FORM_VALID';     // session value class (session array for valid field values)
var $valid_class_name = 'valid_input';
var $invalid_class_name = 'invalid_input';


/* PRIVATE PROPERTIES */
var $_Filter = 0;           // passed from CeoForm

var $_arg5 = null;
var $_arg6 = null;
var $_arg7 = null;
var $_arg8 = null;

var $_value = null;
var $_prompt = '';
var $_html_signal = '';
var $_html_prompt = '';

// child class names
var $_input_text_class_fname = 'input_text.class.php';

var $_filename = '';
var $_dirpath = '';


/* ** MAGIC METHODS ** */
// php4 constructor
function CeoFormElement($name, $type, $label, $is_required=0, $arg5=null, $arg6=null, $arg7=null, $arg8=null)
{
  $this->__construct($name, $type, $label, $is_required, $arg5, $arg6, $arg7, $arg8);
  register_shutdown_function( array($this, '__destruct') );
}
// END constructor

// php5 constructor
function __construct($name, $type, $label, $is_required, $arg5, $arg6, $arg7, $arg8)
{
  // default
  $this->oid = $type . $name;
  $this->_set_filename();
  $this->_set_dirpath();
  
  // define valid concrete element child class
  $this->VALID = $this->get_valid_types();
  
  // set path values

  // assign args
  $this->type = $type;
  $this->name = $name;
  $this->label = $label;
  $this->is_required = ( empty($is_required) ) ? 0 : $is_required;
  $this->_html_signal = $name . '_signal';
  
  // dom props
  $this->class = $type;
  $this->id = "{$type}_{$name}";
  
  // validity check
  if ( !$this->_cx_is_valid() ) return 0;
  
  // if submitted, process input (sanitize) and check validity
  if ( $this->is_submitted() ) 
  {
    if ( $this->debug ) $this->print_d("{$this->name} is submitted -> will process input");
    $this->_process_input();
  }
  
  // debug
  if ( $this->debug ) $this->print_d('debugging is active for oid ' . $this->oid);
  if ( $this->debug ) $this->print_d('constructor complete for class ' . __CLASS__);
}
// END constructor

// destructor
function __destruct()
{
  if ( $this->debug ) $this->print_d("destroying class {$this->class_name} (oid: {$this->oid})");
}
// END destructor



/* ** PUBLIC METHODS ** */
// method: sanitize input
function filter_input($text, $WHITE_TAG=null, $WHITE_ATT=null, $debug=0)
{
  $sanitized_text = '';    // return
  global $CEO;
  if ( $this->debug ) $this->print_d('this is the parent element class filter method using defaults -- may be overriden');
  
  if ( is_null($WHITE_TAG) ) $WHITE_TAG = $CEO['IN']['WHITE']['TAG'];
  if ( is_null($WHITE_TAG) ) $WHITE_ATT = $CEO['IN']['WHITE']['ATT'];
  $sanitized_text = CeoForm::staticInputFilter($text, $CEO['IN']['WHITE']['TAG'], $CEO['IN']['WHITE']['ATT']);

  return $sanitized_text;
}


// method: is submitted
function is_submitted()
{
  $is_submitted = ( isset($_POST[$this->_html_signal]) ) ? 1 : 0;
  return $is_submitted;
}


// method: set valid value
function set_valid_value($value)
{
  $this->valid_input = $value;
  $this->_set_session_value();
  return;
}


// method: unset valid value / invalidate
function unset_valid_value()
{
  $this->is_valid = 0;
  $this->valid_input = null;
  $this->_clear_session_value();
  return;
}


// method: get valid value
function get_valid_value()
{
  return $this->_get_session_value();
}


// method: alias of unset_valid_value
function invalidate()
{
  return $this->unset_valid_value();
}


// method: print block
function reset()
{
  if ( $this->debug ) $this->print_d("resetting form element [{$this->name}]");
  $this->unset_valid_value();
  return;
}


// method: print block
function print_block()
{
  trigger_error($this->_override_warning(__FUNCTION__), E_USER_WARNING);
  return 0;
}

// method: get_valid_types()
function get_valid_types()
{
  $V = array();    // return
  
  // element families
  $V['INPUT_TEXT'] = array('input_text', 'input_email', 'input_url', 'input_password');
  $V['TEXTAREA'] = array('textarea');
  
  // all elements
  $V['TYPE'] = array_merge($V['INPUT_TEXT'],$V['TEXTAREA']);
  
  return $V;
}


// method: static method for build hidden input tag
function static_build_hidden_input($name, $value)
{
  $tag = '';   // return
  $tag = '<input type="hidden" name="'.$name.'" value="'.$value.'"  />';  
  return $tag;
}



// method: print_d
function print_d($message, $color='#c33')
{
  $_D = debug_backtrace();
  $f = basename($_D[0]['file']);
  $l = $_D[0]['line'];
  $loc = "{$f}:{$l}";
  $out = "<div style='line-height:1.5em; font-family:monospace; color:$color;'>$message <span style='color:#666;'>[$loc]</span></div>";
  $this->_DBG[] = "$loc -> " . strip_tags($message);
  echo $out;
  return;
}
// END method

// method: print_r
function print_r($Mixed)
{
  $return = htmlspecialchars(print_r($Mixed, 1));
  $return = "<pre>$return</pre>";
  return $return;
}
// END method

// method: dump
function dump()
{
  echo $this->print_r($this);
  return;
}
// END method



/* ** PRIVATE METHODS ** */
// method: process input
function _process_input()
{
  $input = !empty($_POST[$this->name]) ? $_POST[$this->name] : '';
  $this->_value = empty($input) ? '' : $this->filter_input($input); 
  if ( $this->debug ) $this->print_d(__FUNCTION__ . htmlspecialchars(" : '$input' -> '{$this->_value}'"));
  
  /* as part of standard obj cx, any input is validated and is_valid property
    set.  this can be overridden by the public invalidate method */
  $this->_validate_input();
  
  if ( $this->is_valid ) $this->set_valid_value($this->_value);
  else $this->unset_valid_value();
  
  return;
}
// END method


// method: validate input (called when processing)
function _validate_input()
{
  trigger_error($this->_override_warning(__FUNCTION__), E_USER_WARNING);
  return 0;
}




// method: get display value for print method
function _get_display_value()
{
  $value = '';
  
  // look for session value first (which will also be current valid input)
  $value = $this->_get_session_value();
  
  // if not session/valid input, use latest value
  if ( empty($value) ) $value = $this->_value;
  
  return $value;
}


// method: process
function _set_session_value()
{
  if ( $this->debug ) $this->print_d("storing valid input to session : {$this->name} -> {$this->valid_input}");
  $_SESSION[$svc][$this->name] = $this->valid_input;
  return;
}


// method: process
function _clear_session_value()
{
  if ( $this->debug ) $this->print_d("clearing session valid input value for [{$this->name}]");
  if ( isset($_SESSION[$svc][$this->name]) )
  {
    $_SESSION[$svc][$this->name] = null;
    unset($_SESSION[$svc][$this->name]);
  }
  return;
}


// method: process
function _get_session_value()
{
  if ( $this->debug ) $this->print_d("getting session valid input value for [{$this->name}]");
  $value = null;
  if ( isset($_SESSION[$svc][$this->name]) )  $value = $_SESSION[$svc][$this->name];
  return $value;
}


// method: cx is valid
function _cx_is_valid()
{
  // valid element type
  if ( !in_array($this->type, $this->VALID['TYPE']) )
  {
    trigger_error("type [{$this->type}] is not a valid form element type", E_USER_WARNING);
    return 0;
  }
  
  return 1;
}


// method: process
function _wrap_print_block($core_html, $type, $name, $id=null, $class=null)
{
  $html = '';   // return
  
  if ( $this->debug ) $this->print_d("wrapping print block for $name");
  
  // css settings
  if ( is_null($id) ) $id = $name;
  if ( is_null($class) ) $class = $type;
  $type_class = ( !empty($this->css_class) ) ? $this->css_class : 'type_' . $type;
  
  $html = <<<HTML
  
<div class="ceo_element"><div class="{$type_class}">
<div class="{$class}" id="{$id}">

{$core_html}

</div>
</div></div>
HTML;

  return $html;

}


// method: override warning
function _override_warning($fx='?')
{
  return "this method [$fx] should be overridden in a child class";
}

// method: _set_session_data
function _set_session_data()
{
  // initialize session
  if ( !session_id() ) session_start(); 
  $_SESSION[$this->oid] = array();
  
  // add session data here
  
  return;
}
// END method

// method: get session data
function _get_session_data()
{
  // initialize session
  if ( !$this->_has_session_data() ) return; 
    
  // retrieve session variables
  // $this->var = $_SESSION[$this->oid]['var'];
  
  return;
}
// END method

// method: has session data
function _has_session_data()
{
  // initialize session
  if ( !session_id() ) session_start(); 
    
  // retrieve session variables
  if ( empty($_SESSION[$this->oid]) ) return 0;
  else return 1;
}
// END method

// VALIDATION METHODS
// method: is valid email
function _is_valid_email($email)
{
  $is_valid = 0;  // return
  
  $_EREGI['email'] = "^[a-z0-9]+([_.-][a-z0-9]+)*@([a-z0-9]+([.-][a-z0-9]+)*)+\\.[a-z]{2,4}$";
  
  if ( eregi($_EREGI['email'], $email) ) $is_valid = 1;
  
  return $is_valid;
}

// method: is valid url
// based on drupal: http://api.drupal.org/api/5/function/valid_url
function _is_valid_url($url)
{
  $is_valid = 0;  // return
  
  $_REGEX['url'] = "/^[a-z0-9\/:_\-_\.\?\$,;~=#&%\+]+$/i";
  
  if ( eregi($_REGEX['url'], $url) ) $is_valid = 1;
  
  return $is_valid;
}

function _set_filename() { $this->_filename = basename(__FILE__); }
function _set_dirpath() { $this->_dirpath = dirname(__FILE__) . $this->DS; }

} // end class
/*____________________________________________________________________________*/

?>
